/*
Modifications by: Paul Krushinskie and Cary Rivet
Added:
	void PWD_PRIME();
	void SYS_LS();
	void SYS_LS_L();
	
	cary added:
	CreateDir();
	RemoveDir();
	list of subdirectories
	int getCurrDirSector();
	int currentDirSector;
*/

// filesys.h 
//	Data structures to represent the Nachos file system.
//
//	A file system is a set of files stored on disk, organized
//	into directories.  Operations on the file system have to
//	do with "naming" -- creating, opening, and deleting files,
//	given a textual file name.  Operations on an individual
//	"open" file (read, write, close) are to be found in the OpenFile
//	class (openfile.h).
//
//	We define two separate implementations of the file system. 
//	The "STUB" version just re-defines the Nachos file system 
//	operations as operations on the native UNIX file system on the machine
//	running the Nachos simulation.  This is provided in case the
//	multiprogramming and virtual memory assignments (which make use
//	of the file system) are done before the file system assignment.
//
//	The other version is a "real" file system, built on top of 
//	a disk simulator.  The disk is simulated using the native UNIX 
//	file system (in a file named "DISK"). 
//
//	In the "real" implementation, there are two key data structures used 
//	in the file system.  There is a single "root" directory, listing
//	all of the files in the file system; unlike UNIX, the baseline
//	system does not provide a hierarchical directory structure.  
//	In addition, there is a bitmap for allocating
//	disk sectors.  Both the root directory and the bitmap are themselves
//	stored as files in the Nachos file system -- this causes an interesting
//	bootstrap problem when the simulated disk is initialized. 
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#ifndef FS_H
#define FS_H

#include "copyright.h"
#include "openfile.h"

// Sectors containing the file headers for the bitmap of free sectors,
// and the directory of files.  These file headers are placed in well-known 
// sectors, so that they can be located on boot-up.
#define FreeMapSector 		0
#define DirectorySector 	1



#ifdef FILESYS_STUB 		// Temporarily implement file system calls as 
				// calls to UNIX, until the real file system
				// implementation is available
class FileSystem {
  public:
    FileSystem(bool format) {}

    bool Create(char *name, int initialSize) { 
	int fileDescriptor = OpenForWrite(name);

	if (fileDescriptor == -1) return FALSE;
	Close(fileDescriptor); 
	return TRUE; 
	}

    OpenFile* Open(char *name) {
	  int fileDescriptor = OpenForReadWrite(name, FALSE);

	  if (fileDescriptor == -1) return NULL;
	  return new OpenFile(fileDescriptor);
    }
	  
    bool Remove(char *name) { return Unlink(name) == 0; }

};

#else // FILESYS




// begin Modifications By Cary RIvet for project 4

// information on sub directories
// 			If parent is null then subDirectory is a child of the root directory
struct dirNode{
	
	char *name;	// the name of the current directory
	dirNode *next;		// the next node in the list
	char *parentName;	// this directory's parent directory's name
	int parentSector;		// sector of parent(since it is the key)
	int sector;
	
	// adding adding parentless subDirectory (root child)
	dirNode(char * newName, int dirSector){
		// assuming a check for a valid newName before creating dirNode
		name = new char[20];
		sprintf(name, "%s", newName);
		
		parentName = NULL;
		next = NULL;						
		sector = dirSector;
		parentSector = 1; // root directory's sector
	}
	
	// adding a directory with a parent (sub subdirectory)
	dirNode(char * newName, char * parent, int parentSec, int dirSector){
		
		// assuming a check for a valid newName before creating dirNode
		name = new char[20];
		sprintf(name, "%s", newName);


		
		if(parent != NULL){
			parentName = new char[20];		
			sprintf(parentName, "%s", parent);		// read in the parent name
		}else{
			parent == NULL;
		}
		
		next = NULL;					
		sector = dirSector;
		parentSector = parentSec;
	}

	
	
};

// linked list of sub directories
class DirList{
	public:
		// constructor initilizes first to NULL (no sub directories)
		DirList(){
			first = NULL;
			size = 0;
		}  
		
		~DirList(){
			// remove all children and deallocate them
			printf("DIR LIST DESTRUCT CALLED\n");
			delete first;
		}
		int getSize(){
			return size;
		}
		
		// add a sub-subdirectory
		void addDir(char* newDir, char* parentName, int parentSector, int sectorNum){
			
			// check for a valid newDir name before continuing
			if(newDir == NULL){
				printf("Invalid Directory Name. Not adding directory\n");
				return;
			}
					
			//if the list is empty create new node for the first child
			if(isEmpty()){
				// this case doesn't really make sense in the grand scheme of things,
				//		but hey, safety right?
				//printf("Adding to empty DIR list\n");
				first = new dirNode(newDir, parentName, parentSector, sectorNum);
				size++;
				return;
			}else{
				// iterate through the list and if babyName is not unique
				//	notify user and exit, otherwise add it to the end of the list
				dirNode* temp ;			
				for(temp= first; temp != NULL; temp = temp->next){	
					// ensure unique keys
					if(temp->sector == sectorNum){
						printf("Directory already exists, not creating\n");
						return;						
					}				
				}
				
				// go until null
				temp = first;
				while(temp->next != NULL){
					temp = temp->next;	
				}
				
				// add child to the end of the list
				temp->next = new dirNode(newDir, parentName, parentSector, sectorNum);
				
				//printf("\n-------------------------\n");
				//printf("The List now is: \n");
				
				for(dirNode *currNode = first; currNode != NULL; currNode = currNode->next){
				//	printf("NAME: %s \n", currNode->name);
				//	printf("Sector: %d\n", currNode->sector);
				}
			//	printf("Printed the list: \n");
			//	printf("\n-------------------------\n");
				
				//printf("ADDED temp: %s\n", temp->name);
				size++;
			}
			
		
		}
		
		// MAY NEED ANOTHER ONE OF THESE TO ACCOUNT FOR ADDING TO PATHS
		
		// add a directory to the root directory
		void addDir(char* newDir, int sectorNum){
			
			// check for a valid newDir name before continuing
			if(newDir == NULL){
				printf("Invalid Directory Name. Not adding directory\n");
				return;
			}
			
			//if the list is empty create new node for the first child
			if(isEmpty()){
				first = new dirNode(newDir,sectorNum);
				size++;
				return;
			}else{
				// iterate through the list and if babyName is not unique
				//	notify user and exit, otherwise add it to the end of the list
				dirNode* temp ;			
				for(temp = first; temp != NULL; temp = temp->next){
					// ensure unique keys
					if(temp->sector == sectorNum){
						printf("Directory already exists, not creating\n");
						return;						
					}			
				}
				
				// add child to the end of the list
				temp = new dirNode(newDir,sectorNum);
				size++;
			}
			
		
		}
		
		// MAY NEED ANOTHER ONE OF THESE TO ACCOUNT FOR REMOVING FROM PATHS
		
		void rmDir(int removalsSector){
			printf("\nAttempting to remove directory in sector %d.\n", removalsSector);
			
			if(isEmpty()){
				return;
			}
			
			if(first->sector == removalsSector){
				dirNode* temp = first;
				first = first->next;
				delete temp;
				size--;
			
			}else{		// otherwise
				dirNode* temp ;
				dirNode* gtfo = NULL;
				
				// iterate through the list 
				for(temp = first; temp != NULL ; temp = temp->next){
					// if find the file in the list 
					//	mark the file for deletion and exit loop
					if(temp->sector == removalsSector){
						gtfo = temp;
						break;
					}					
				}
				
				// if file is found notify of removal and deallocate the node
				if(gtfo != NULL){
					temp = temp->next;
					delete gtfo;
					size--;
										
				}else{
					printf("Unable to find directory in sector %d in directory list, NOT REMOVING.\n",removalsSector);
				}				
			}
		}
		
		
		bool find(int chosenSector){
			
			for(dirNode* temp = first; temp != NULL; temp = temp->next){
				if(temp->sector == chosenSector)
					return true;
			}
			return false;
			
		}	
		
		char *getParentName(int childsSector){
			
			for(dirNode* temp = first; temp != NULL; temp = temp->next){
				if(temp->sector == childsSector)
					return temp->parentName;
			}
			return NULL;
			
		}
		
		
		char *getName(int currSector){
			for(dirNode* temp = first; temp != NULL; temp = temp->next){
				if(temp->sector == currSector)
					return temp->name;
			}
			return NULL;
		}
		
		int getParentSector(int childsSector){
			
			for(dirNode* temp = first; temp != NULL; temp = temp->next){
				if(temp->sector == childsSector)
					return temp->parentSector;
			}
			return -1;
			
		}
		
		
		bool isEmpty(){
			if(size == 0)
				return true;
			else
				return false;		
		}
		
		
		char* getPath(int dirSector){
			char *path;
		
			printf("In get path\n");
			// find out depth of current 
			int depth = getDepth(dirSector);
			
			// make path large enough to accomidate the entire path (accomidating the slashes)
			path = new char[((depth*20) + 1)];
			for(int x = 0; x < ((depth*20) + 1); x++){
				path[x] = NULL;
			}
			
			
			// create an array of char* to hold all of the parent names
			char **parents;
			parents = new char*[depth];
			
			int currentParent =  dirSector;
			
			printf("First FOR in getPath\n");
			

			for(int i = 0; i < depth; i++){
				printf("Doing it %d \n", i);
				currentParent = getParentSector(dirSector);
				if(getParentSector(dirSector) == 1){
					parents[i] = new char[20];
					if(strcmp(parents[i], "/") != 0)
						sprintf(parents[i], "/");
				}
				else{
					parents[i] = new char[20];
					sprintf(parents[i], getParentName(dirSector));
				}
			
			}
			printf("Depth: %d", depth);
			
			for(int i = 0; i < depth; i++){
				printf("%s ", parents[i]);
			}
			
			printf("\nfinished for\n");
			//sprintf(path, parents[depth--]);
			// concatinate the path strings
			printf("Path B4: %s \n", path);
			
			for(int i = depth; i >= 0; i--){
				
				if(parents[i] == NULL){
					strcat(path, "/");
				}else{
					strcat(path, parents[i]);
					strcat(path,"/");		// add a back slash 
				}
				
			}
			
			printf("Path After %s \n", path);
			
			return path;
		}
		
		// returns number of parent directorys until root dir
		// 		returns 0 if root, 1 for sub in root, etc
		int getDepth(int dirSector){
			
			int depth = 0;
			int SectorIT = getParentSector(dirSector);
			
			// traverse through parents and count
			while(SectorIT != -1){
				depth++;
				printf("SectorIT %d \n", SectorIT);
				//printf("Depth: %d \n", depth);
				SectorIT = getParentSector(dirSector);			
				dirSector = SectorIT;
			}
			
			return depth-1;
			
		}
		
		dirNode* getFirst(){
			return first;
		}
		
		
		
		dirNode *first;	// the first added directory of the list
		int size;
		
};


class FileSystem {
  public:
    FileSystem(bool format);		// Initialize the file system.
					// Must be called *after* "synchDisk" 
					// has been initialized.
    					// If "format", there is nothing on
					// the disk, so initialize the directory
    					// and the bitmap of free blocks.

    bool Create(char *name, int initialSize);  	
					// Create a file (UNIX creat)
	
	
    OpenFile* Open(char *name); 	// Open a file (UNIX open)

    bool Remove(char *name);  		// Delete a file (UNIX unlink)
	
	bool CreateDir(char *name);
	
	bool CreateDirByPath(char *name, char *path);
	
	bool changeDir(int chgDir);

	bool changeDirNoPath(char *path);
	
	bool RemoveDir(char *name); 
	
	int getCurrDirSector();	// added to return the current working directory's sector
	
    void List();			// List all the files in the file system

    void Print();			// List all the files and their contents
	
	void PWD_PRIME();
	
	void SYS_LS();
	
	void SYS_LS_L();
	
	void AllowSysCall();
	
	void RevertSysCall();
	
	void IncreaseFileLength(char *name, int num);
	
	int parsePath(char* path);
	
	OpenFile* getOpenFileBySect(char* name, int sector);
	
	bool CreateForDirectory(int size, char* name, int sect);
	
	void changeToRoot();
	
	void removeFromDir(char *name, int sect);
	
	
//	void CloseFile(OpenFile*);

  private:
   OpenFile* freeMapFile;		// Bit map of free disk blocks,
					// represented as a file
   OpenFile* directoryFile;		// "Root" directory -- list of 
					// file names, represented as a file
					
	OpenFile* rootFile;

	int rootSector;	
					
	int firstSect;
	
	OpenFile* firstFile;

	OpenFile* writeFile;
					
	DirList *myDirList;
	
	int writeFileSect;
	
	int currentDirSector; // for the ablility to keep track of the current directory's sector (for dirList traversal and adding)
	
	int currSpotInDoc;
	
	
	// End Modifications by Cary Rivet for Project 4
					
};

#endif // FILESYS

#endif // FS_H
